home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SuperHack
/
SuperHack CD.bin
/
CODING
/
CPP
/
SETTIME.ZIP
/
CWINSOCK.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1995-12-19
|
52KB
|
1,717 lines
// cwinsock.cpp : implementation file
//
#include "afxwin.h"
#include "afxext.h"
#include <memory.h>
#include <stdlib.h>
#include "cwinsock.h"
#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CWinSock
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// CWinSock constructor
//
// Constructs the CWinSock object. Initializes member variables
//
CWinSock::CWinSock(WORD wVersionRequired/*= MAKEWORD(1, 1)*/)
{
// initialize member variables
m_wVersionRequired = wVersionRequired;
m_nLastError = 0;
}
/////////////////////////////////////////////////////////////////////////////
// CWinSock::Startup()
//
// Start the WinSock sub-system.
//
int CWinSock::Startup()
{
int nStatus = CWINSOCK_NOERROR;
m_nLastError = WSAStartup(m_wVersionRequired, &m_wsaData);
if (m_nLastError != 0)
nStatus = CWINSOCK_WINSOCK_ERROR;
return nStatus;
}
/////////////////////////////////////////////////////////////////////////////
// CWinSock::Shutdown()
//
// Shutdown the WinSock sub-system.
//
int CWinSock::Shutdown()
{
int nStatus = CWINSOCK_NOERROR;
if (WSACleanup() != 0)
{
m_nLastError = WSAGetLastError();
nStatus = CWINSOCK_WINSOCK_ERROR;
}
return nStatus;
}
/////////////////////////////////////////////////////////////////////////////
// CWinSock::Information()
//
// Copy the WinSock information structure.
//
void CWinSock::Information(LPWSADATA pwsaData)
{
memcpy(pwsaData, &m_wsaData, sizeof(WSADATA));
}
/////////////////////////////////////////////////////////////////////////////
// CDatagramSocket
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// CDatagramSocket constructor
//
// Constructs the CDatagramSocket object. Initializes member variables
//
CDatagramSocket::CDatagramSocket(CWnd *pParentWnd, UINT uMsg)
{
// initialize member variables
m_pParentWnd = pParentWnd;
ASSERT(m_pParentWnd != NULL);
m_uMsg = uMsg;
ASSERT(m_uMsg != 0);
InitVars();
}
/////////////////////////////////////////////////////////////////////////////
// CDatagramSocket destructor
//
CDatagramSocket::~CDatagramSocket()
{
}
/////////////////////////////////////////////////////////////////////////////
// CDatagramSocket::InitVars()
//
// Initialize class member variables.
//
void CDatagramSocket::InitVars(BOOL bInitLastError/*= TRUE*/)
{
if (bInitLastError)
m_nLastError = 0;
m_s = INVALID_SOCKET;
memset(&m_sinLocal, 0, sizeof(SOCKADDR_IN));
m_bServer = FALSE;
}
/////////////////////////////////////////////////////////////////////////////
// CDatagramSocket::CreateSocket()
//
// Create a hidden window that will receive asynchronous messages
// from WinSock. Also creates a socket and optionally binds it to
// a name if the socket is a server socket.
//
// This version of the CreateSocket() function takes a
// port number, in host order, as input. A port number
// should only be specified if the socket is to be bound
// to a certain port. If you don't care which port is
// assigned to the socket, just call CreateSocket() without
// any parameter, causing CreateSocket(NULL) to be called.
//
int CDatagramSocket::CreateSocket(int nLocalPort)
{
// if this version of the function is being called,
// a valid port number must be specified
if (nLocalPort <= 0)
return CWINSOCK_PROGRAMMING_ERROR;
// convert the port number into a string and
// call the version of CreateSocket() which
// accepts a string
char pszLocalService[18];
_itoa(nLocalPort, pszLocalService, 10);
return CreateSocket(pszLocalService);
}
/////////////////////////////////////////////////////////////////////////////
// CDatagramSocket::CreateSocket()
//
// Create a hidden window that will receive asynchronous messages
// from WinSock. Also creates a socket and optionally binds it to
// a name if the socket is a server socket.
//
// This version of the CreateSocket() function takes a
// string containing a service name or port number.
// A parameter should only be specified if the socket is to be
// bound to a certain port. If you don't care which port is
// assigned to the socket, just call CreateSocket() without
// any parameter, causing CreateSocket(NULL) to be called.
//
int CDatagramSocket::CreateSocket(LPSTR pszLocalService/*= NULL*/)
{
int nStatus = CWINSOCK_NOERROR;
while (1)
{
// Make sure the socket isn't already created.
// If the socket handle is valid, return from this
// function right away so the existing parameters of
// the object are not tampered with.
if (m_s != INVALID_SOCKET)
return CWINSOCK_PROGRAMMING_ERROR;
InitVars();
// create the hidden window
RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = 100;
rect.bottom = 100;
if (Create(NULL, NULL, WS_OVERLAPPEDWINDOW, rect, m_pParentWnd, 0) == 0)
{
nStatus = CWINSOCK_WINDOWS_ERROR;
break;
}
// create the socket
m_s = socket(PF_INET, SOCK_DGRAM, 0);
if (m_s == INVALID_SOCKET)
{
m_nLastError = WSAGetLastError();
nStatus = CWINSOCK_WINSOCK_ERROR;
DestroyWindow();
break;
}
// If pszLocalService is not NULL, this is a server socket
// that will accept data on the specified port.
if (pszLocalService != NULL)
{
// this socket is bound to a port number
// so set the server flag
m_bServer = TRUE;
// assign the address family
m_sinLocal.sin_family = AF_INET;
// assign the service port (may have to do a database lookup
// if a service port number was not specified)
m_sinLocal.sin_port = htons(atoi(pszLocalService));
if (m_sinLocal.sin_port == 0)
{
LPSERVENT pSent = getservbyname(pszLocalService, "udp");
if (pSent == NULL)
{
m_nLastError = WSAGetLastError();
nStatus = CWINSOCK_WINSOCK_ERROR;
closesocket(m_s);
DestroyWindow();
break;
}
m_sinLocal.sin_port = pSent->s_port;
}
// assign the IP address
m_sinLocal.sin_addr.s_addr = htonl(INADDR_ANY);
// bind the server socket to the name containing the port
if (bind(m_s, (LPSOCKADDR)&m_sinLocal, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
{
m_nLastError = WSAGetLastError();
nStatus = CWINSOCK_WINSOCK_ERROR;
closesocket(m_s);
DestroyWindow();
break;
}
}
// start asynchronous event notification
long lEvent = FD_READ | FD_WRITE;
if (WSAAsyncSelect(m_s, m_hWnd, CWINSOCK_EVENT_NOTIFICATION, lEvent) ==
SOCKET_ERROR)
{
m_nLastError = WSAGetLastError();
nStatus = CWINSOCK_WINSOCK_ERROR;
closesocket(m_s);
DestroySocket();
break;
}
break;
}
// if anything failed in this function, set the
// socket variables appropriately
if (nStatus != CWINSOCK_NOERROR)
InitVars(FALSE);
return nStatus;
}
/////////////////////////////////////////////////////////////////////////////
// CDatagramSocket::DestroySocket()
//
// Close the socket, remove any queued data,
// and destroy the hidden window.
//
int CDatagramSocket::DestroySocket()
{
int nStatus = CWINSOCK_NOERROR;
// make sure the socket is valid
if (m_s == INVALID_SOCKET)
nStatus = CWINSOCK_PROGRAMMING_ERROR;
else
{
// remove any data in the write queue
while (!m_listWrite.IsEmpty())
{
LPDATAGRAMDATA pDatagramData = (LPDATAGRAMDATA)m_listWrite.RemoveHead();
LPVOID pData = pDatagramData->pData;
delete pDatagramData;
m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_WRITING,
(LPARAM)pData);
}
// remove any data in the read queue
while (!m_listRead.IsEmpty())
{
LPDATAGRAMDATA pDatagramData = (LPDATAGRAMDATA)m_listRead.RemoveHead();
free(pDatagramData->pData);
delete pDatagramData;
}
// close the socket and initialize variables
closesocket(m_s);
InitVars();
// destroy the hidden window
DestroyWindow();
}
return nStatus;
}
/////////////////////////////////////////////////////////////////////////////
// CDatagramSocket::Write()
//
// Write data to the socket specified by the name and port.
//
// This version of the Write() function takes an integer
// representing the length of the data to send, a pointer
// to the data to send, a pointer to a string representing
// the host name to send the data to, and an integer
// representing the port number to send to.
//
// The data pointed to by pData must remain valid until either
// the Write() function returns with an error, or the
// write's completion is notified by the m_uMsg being sent
// to the window that owns this datagram object with wParam set
// to CWINSOCK_DONE_WRITING or CWINSOCK_ERROR_WRITING.
//
int CDatagramSocket::Write(int nLen, LPVOID pData,
LPSTR pszRemoteName, int nRemotePort)
{
// convert the port number into a string and
// call the version of Write() which accepts
// a string service name or number
char pszRemoteService[18];
_itoa(nRemotePort, pszRemoteService, 10);
return Write(nLen, pData, pszRemoteName, pszRemoteService);
}
/////////////////////////////////////////////////////////////////////////////
// CDatagramSocket::Write()
//
// Write data to the socket specified by the name and service
// name or number.
//
// This version of the Write() function takes an integer
// representing the length of the data to send, a pointer
// to the data to send, a pointer to a string representing
// the host name to send the data to, and a string representing
// the service name or port number to send the data to.
//
// The data pointed to by pData must remain valid until either
// the Write() function returns with an error, or the
// write's completion is notified by the m_uMsg being sent
// to the window that owns this datagram object with wParam set
// to CWINSOCK_DONE_WRITING or CWINSOCK_ERROR_WRITING.
//
int CDatagramSocket::Write(int nLen, LPVOID pData,
LPSTR pszRemoteName, LPSTR pszRemoteService)
{
int nStatus = CWINSOCK_NOERROR; // error status
LPHOSTENT pHent; // pointer to host entry structure
LPSERVENT pSent; // pointer to service entry structure
SOCKADDR_IN sinRemote; // Internet address of destination
while (1)
{
// assign the address family
sinRemote.sin_family = AF_INET;
// assign the service port (may have to do a database lookup
// if a service port number was not specified)
sinRemote.sin_port = htons(atoi(pszRemoteService));
if (sinRemote.sin_port == 0)
{
pSent = getservbyname(pszRemoteService, "udp");
if (pSent == NULL)
{
m_nLastError = WSAGetLastError();
nStatus = CWINSOCK_WINSOCK_ERROR;
break;
}
sinRemote.sin_port = pSent->s_port;
}
// assign the IP address (may have to do a database lookup
// if a dotted decimal IP address was not specified)
sinRemote.sin_addr.s_addr = inet_addr(pszRemoteName);
if (sinRemote.sin_addr.s_addr == INADDR_NONE)
{
pHent = gethostbyname(pszRemoteName);
if (pHent == NULL)
{
m_nLastError = WSAGetLastError();
nStatus = CWINSOCK_WINSOCK_ERROR;
break;
}
sinRemote.sin_addr.s_addr = *(u_long *)pHent->h_addr;
}
// call the version of Write() that takes an
// Internet address structure
return Write(nLen, pData, &sinRemote);
}
return nStatus;
}
/////////////////////////////////////////////////////////////////////////////
// CDatagramSocket::Write()
//
// Write data to the socket specified by the Internet address.
//
// This version of the Write() function takes an integer
// representing the length of the data to send, a pointer
// to the data to send, and a pointer to an Internet address
// structure to send the data to.
//
// The data pointed to by pData must remain valid until either
// the Write() function returns with an error, or the
// write's completion is notified by the m_uMsg being sent
// to the window that owns this datagram object with wParam set
// to CWINSOCK_DONE_WRITING or CWINSOCK_ERROR_WRITING.
//
int CDatagramSocket::Write(int nLen, LPVOID pData, LPSOCKADDR_IN psinRemote)
{
int nStatus = CWINSOCK_NOERROR;
while (1)
{
// dynamically allocate a structure to hold the
// data pointer, the data's length, and the destination address
LPDATAGRAMDATA pDatagramData = new DATAGRAMDATA;
if (pDatagramData == NULL)
{
nStatus = CWINSOCK_WINDOWS_ERROR;
break;
}
pDatagramData->pData = pData;
pDatagramData->nLen = nLen;
memcpy(&(pDatagramData->sin), psinRemote, sizeof(SOCKADDR_IN));
// add the data to the list
TRY
{
m_listWrite.AddTail(pDatagramData);
}
CATCH (CMemoryException, e)
{
nStatus = CWINSOCK_WINDOWS_ERROR;
break;
}
END_CATCH
// trigger the FD_WRITE handler to try to send
PostMessage(CWINSOCK_EVENT_NOTIFICATION, m_s, WSAMAKESELECTREPLY(FD_WRITE, 0));
break;
}
return nStatus;
}
/////////////////////////////////////////////////////////////////////////////
// CDatagramSocket::Read()
//
// Read data that has been received by the socket.
//
// This function takes a pointer to an integer that will be filled
// with the length of the data read and an optional pointer
// to an Internet address structure that will be filled with
// the address of the sender of the data.
//
// A pointer to the data is returned on success. The application
// using this object must free this pointer. NULL is returned on failure.
//
LPVOID CDatagramSocket::Read(LPINT pnLen, LPSOCKADDR_IN psinRemote/*= NULL*/)
{
LPVOID pData = NULL;
// check to see if there is data to retrieve
if (!m_listRead.IsEmpty())
{
// remove the stream data from the list
LPDATAGRAMDATA pDatagramData = (LPDATAGRAMDATA)m_listRead.RemoveHead();
pData = pDatagramData->pData;
*pnLen = pDatagramData->nLen;
if (psinRemote != NULL)
memcpy(psinRemote, &(pDatagramData->sin), sizeof(SOCKADDR_IN));
delete pDatagramData;
}
return pData;
}
// message map
BEGIN_MESSAGE_MAP(CDatagramSocket, CWnd)
//{{AFX_MSG_MAP(CDatagramSocket)
//}}AFX_MSG_MAP
ON_MESSAGE(CWINSOCK_EVENT_NOTIFICATION, OnWinSockEvent)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CDatagramSocket::OnWinSockEvent()
//
// Called when there is an asynchronous event on the socket.
//
LONG CDatagramSocket::OnWinSockEvent(WPARAM wParam, LPARAM lParam)
{
// check for an error
if (WSAGETSELECTERROR(lParam) != 0)
return 0L;
// what event are we being notified of?
switch (WSAGETSELECTEVENT(lParam))
{
case FD_READ:
return HandleRead(wParam, lParam);
break;
case FD_WRITE:
return HandleWrite(wParam, lParam);
break;
default:
// this should never happen
ASSERT(0);
break;
}
return 0L;
}
/////////////////////////////////////////////////////////////////////////////
// CDatagramSocket::HandleRead()
//
// Called when there is an asynchronous read event on the socket.
//
// If the read was successful, the data, its length, and the address
// of the sender of the data, are stored in the read queue. Upon
// a successful read, the application window using this object is
// then notified with the m_uMsg message (wParam set to
// CWINSOCK_DONE_READING; lParam set to the number of data chunks
// in the read queue). At this point, the application should call
// Read(). If the read fails for some reason, the m_uMsg is sent
// with wParam set to CWINSOCK_ERROR_READING.
//
LONG CDatagramSocket::HandleRead(WPARAM wParam, LPARAM lParam)
{
while (1)
{
// allocate memory for incoming data
LPVOID pData = malloc(READ_BUF_LEN);
LPDATAGRAMDATA pDatagramData = new DATAGRAMDATA;
if ((pData == NULL) || (pDatagramData == NULL))
{
// free anything that was allocated
if (pData != NULL)
free(pData);
pData = NULL;
if (pDatagramData != NULL)
delete pDatagramData;
pDatagramData = NULL;
// tell the parent that a possible data read failed
m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_READING);
// fake the event to try again
PostMessage(CWINSOCK_EVENT_NOTIFICATION, m_s,
WSAMAKESELECTREPLY(FD_READ, 0));
break;
}
// receive data
int nAddrLen = sizeof(SOCKADDR_IN);
int nBytesRead = recvfrom(m_s, (LPSTR)pData, READ_BUF_LEN, 0,
(LPSOCKADDR)&(pDatagramData->sin), &nAddrLen);
if (nBytesRead == SOCKET_ERROR)
{
// free memory for incoming data
free(pData);
pData = NULL;
delete pDatagramData;
pDatagramData = NULL;
// if the error is just that the read would block,
// don't do anything; we'll get another FD_READ soon
m_nLastError = WSAGetLastError();
if (m_nLastError == WSAEWOULDBLOCK)
m_nLastError = 0;
else
// tell the parent that a data read failed
m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_READING);
break;
}
// add the data to the list
pDatagramData->pData = pData;
pDatagramData->nLen = nBytesRead;
TRY
{
m_listRead.AddTail(pDatagramData);
}
CATCH (CMemoryException, e)
{
free(pData);
pData = NULL;
delete pDatagramData;
pDatagramData = NULL;
// tell the parent that a data read failed
m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_READING);
break;
}
END_CATCH
// tell the parent that data has been read
m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_DONE_READING,
(LPARAM)m_listRead.GetCount());
break;
}
return 0L;
}
/////////////////////////////////////////////////////////////////////////////
// CDatagramSocket::HandleWrite()
//
// Called when there is an asynchronous write event on the socket.
//
// If there is data in the write queue waiting to be sent,
// a WinSock send is attempted. If the send is successful,
// a m_uMsg message is sent to the application window with
// wParam set to CWINSOCK_DONE_WRITING and lParam set to the
// address of the data that was sent. On send failure,
// wParam is set to CWINSOCK_ERROR_WRITING and lParam set to
// the address of the data which couldn't be sent. In either
// case, the application may free the pointer pointing to
// the data or reuse that data buffer.
//
LONG CDatagramSocket::HandleWrite(WPARAM wParam, LPARAM lParam)
{
while (1)
{
// check to see if there is any data to send
if (m_listWrite.IsEmpty())
break;
// get pointers to data, data length, and destination address
LPDATAGRAMDATA pDatagramData = (LPDATAGRAMDATA)m_listWrite.GetHead();
LPVOID pData = pDatagramData->pData;
int nLen = pDatagramData->nLen;
SOCKADDR_IN sin;
memcpy(&sin, &(pDatagramData->sin), sizeof(SOCKADDR_IN));
// send the data
BOOL bRemove = FALSE; // remove data from queue?
int nBytesSent = sendto(m_s, (LPCSTR)pData, nLen, 0,
(LPSOCKADDR)&sin, sizeof(SOCKADDR_IN));
if (nBytesSent == SOCKET_ERROR)
{
// if the error is just that the send would block,
// don't do anything; we'll get another FD_WRITE soon
m_nLastError = WSAGetLastError();
if (m_nLastError == WSAEWOULDBLOCK)
m_nLastError = 0;
else
{
bRemove = TRUE;
m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_WRITING,
(LPARAM)pData);
}
}
else
{
// if data was sent, we must still check to see
// if all the bytes were sent
bRemove = TRUE;
if (nBytesSent == nLen)
m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_DONE_WRITING,
(LPARAM)pData);
else
m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_WRITING,
(LPARAM)pData);
}
// if the data was sent or there was a real
// error, remove the data from the queue
if (bRemove)
{
delete pDatagramData;
m_listWrite.RemoveHead();
}
// if there is more data to send, trigger this FD_WRITE handler
if (!m_listWrite.IsEmpty())
PostMessage(CWINSOCK_EVENT_NOTIFICATION, m_s,
WSAMAKESELECTREPLY(FD_WRITE, 0));
break;
}
return 0L;
}
/////////////////////////////////////////////////////////////////////////////
// CStreamSocket
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// CStreamSocket constructor
//
// Constructs the CStreamSocket object. Initializes member variables
//
CStreamSocket::CStreamSocket(CWnd *pParentWnd, UINT uMsg)
{
m_pParentWnd = pParentWnd;
ASSERT(m_pParentWnd != NULL);
m_uMsg = uMsg;
ASSERT(m_uMsg != 0);
InitVars();
}
/////////////////////////////////////////////////////////////////////////////
// CStreamSocket destructor
//
CStreamSocket::~CStreamSocket()
{
}
/////////////////////////////////////////////////////////////////////////////
// CStreamSocket::InitVars()
//
// Initialize class member variables.
//
void CStreamSocket::InitVars(BOOL bInitLastError/*= TRUE*/)
{
if (bInitLastError)
m_nLastError = 0;
m_s = INVALID_SOCKET;
memset(&m_sinLocal, 0, sizeof(SOCKADDR_IN));
memset(&m_sinRemote, 0, sizeof(SOCKADDR_IN));
m_bServer = FALSE;
}
/////////////////////////////////////////////////////////////////////////////
// CStreamSocket::CreateSocket()
//
// Create a hidden window that will receive asynchronous messages
// from WinSock. Also creates a socket and optionally binds it to
// a name if the socket is a server socket.
//
// This version of the CreateSocket() function takes a
// port number, in host order, as input. A port number
// should only be specified if the socket is to be bound
// to a certain port. If you don't care which port is
// assigned to the socket, just call CreateSocket() without
// any parameter, causing CreateSocket(NULL) to be called.
//
int CStreamSocket::CreateSocket(int nLocalPort)
{
// if this version of the function is being called,
// a valid port number must be specified
if (nLocalPort <= 0)
return CWINSOCK_PROGRAMMING_ERROR;
// convert the port number into a string and
// call the version of CreateSocket() which
// accepts a string
char pszLocalService[18];
_itoa(nLocalPort, pszLocalService, 10);
return CreateSocket(pszLocalService);
}
/////////////////////////////////////////////////////////////////////////////
// CStreamSocket::CreateSocket()
//
// Create a hidden window that will receive asynchronous messages
// from WinSock. Also creates a socket and optionally binds it to
// a name if the socket is a server socket.
//
// This version of the CreateSocket() function takes a
// string containing a service name or port number.
// A parameter should only be specified if the socket is to be
// bound to a certain port. If you don't care which port is
// assigned to the socket, just call CreateSocket() without
// any parameter, causing CreateSocket(NULL) to be called.
//
int CStreamSocket::CreateSocket(LPSTR pszLocalService/*= NULL*/)
{
int nStatus = CWINSOCK_NOERROR;
while (1)
{
// Make sure the socket isn't already created.
// If the socket handle is valid, return from this
// function right away so the existing parameters of
// the object are not tampered with.
if (m_s != INVALID_SOCKET)
return CWINSOCK_PROGRAMMING_ERROR;
InitVars();
// create the hidden window
RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = 100;
rect.bottom = 100;
if (Create(NULL, NULL, WS_OVERLAPPEDWINDOW, rect, m_pParentWnd, 0) == 0)
{
nStatus = CWINSOCK_WINDOWS_ERROR;
break;
}
// create the socket
m_s = socket(PF_INET, SOCK_STREAM, 0);
if (m_s == INVALID_SOCKET)
{
m_nLastError = WSAGetLastError();
nStatus = CWINSOCK_WINSOCK_ERROR;
DestroyWindow();
break;
}
// If pszLocalService is not NULL, this is a server socket
// that will accept data on the specified port.
if (pszLocalService != NULL)
{
// this socket is bound to a port number
// so set the server flag
m_bServer = TRUE;
// assign the address family
m_sinLocal.sin_family = AF_INET;
// assign the service port (may have to do a database lookup
// if a service port number was not specified)
m_sinLocal.sin_port = htons(atoi(pszLocalService));
if (m_sinLocal.sin_port == 0)
{
LPSERVENT pSent = getservbyname(pszLocalService, "tcp");
if (pSent == NULL)
{
m_nLastError = WSAGetLastError();
nStatus = CWINSOCK_WINSOCK_ERROR;
closesocket(m_s);
DestroyWindow();
break;
}
m_sinLocal.sin_port = pSent->s_port;
}
// assign the IP address
m_sinLocal.sin_addr.s_addr = htonl(INADDR_ANY);
// bind the server socket to the name containing the port
if (bind(m_s, (LPSOCKADDR)&m_sinLocal, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
{
m_nLastError = WSAGetLastError();
nStatus = CWINSOCK_WINSOCK_ERROR;
closesocket(m_s);
DestroyWindow();
break;
}
}
// start asynchronous event notification
long lEvent;
if (m_bServer)
lEvent = FD_READ | FD_WRITE | FD_ACCEPT | FD_CLOSE;
else
lEvent = FD_READ | FD_WRITE | FD_CONNECT | FD_CLOSE;
if (WSAAsyncSelect(m_s, m_hWnd, CWINSOCK_EVENT_NOTIFICATION, lEvent) ==
SOCKET_ERROR)
{
m_nLastError = WSAGetLastError();
nStatus = CWINSOCK_WINSOCK_ERROR;
closesocket(m_s);
DestroySocket();
break;
}
// if this is a server, listen for client connections
if (m_bServer)
{
if (listen(m_s, 3) == SOCKET_ERROR)
{
m_nLastError = WSAGetLastError();
nStatus = CWINSOCK_WINSOCK_ERROR;
closesocket(m_s);
DestroySocket();
break;
}
}
break;
}
// if anything failed in this function, set the
// socket variables appropriately
if (nStatus != CWINSOCK_NOERROR)
InitVars(FALSE);
return nStatus;
}
/////////////////////////////////////////////////////////////////////////////
// CStreamSocket::DestroySocket()
//
// Close the socket, remove any queued data,
// and destroy the hidden window.
//
int CStreamSocket::DestroySocket()
{
int nStatus = CWINSOCK_NOERROR;
// make sure the socket is valid
if (m_s == INVALID_SOCKET)
nStatus = CWINSOCK_PROGRAMMING_ERROR;
else
{
// remove any data in the write queue
while (!m_listWrite.IsEmpty())
{
LPSTREAMDATA pStreamData = (LPSTREAMDATA)m_listWrite.RemoveHead();
LPVOID pData = pStreamData->pData;
delete pStreamData;
m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_WRITING,
(LPARAM)pData);
}
// remove any data in the read queue
while (!m_listRead.IsEmpty())
{
LPSTREAMDATA pStreamData = (LPSTREAMDATA)m_listRead.RemoveHead();
free(pStreamData->pData);
delete pStreamData;
}
// close the socket and initialize variables
closesocket(m_s);
InitVars();
// destroy the hidden window
DestroyWindow();
}
return nStatus;
}
/////////////////////////////////////////////////////////////////////////////
// CStreamSocket::Connect()
//
// Connect the client socket to a server specified by the name and port.
//
// This version of the Conncet() function takes a pointer to a
// string representing the host name to send the data to and
// an integer representing the port number to connect to.
//
int CStreamSocket::Connect(LPSTR pszRemoteName, int nRemotePort)
{
// convert the port number into a string and
// call the version of Connect() which accepts
// a string service name or number
char pszRemoteService[18];
_itoa(nRemotePort, pszRemoteService, 10);
return Connect(pszRemoteName, pszRemoteService);
}
/////////////////////////////////////////////////////////////////////////////
// CStreamSocket::Connect()
//
// Connect the client socket to a server specified by the name and
// service name or port.
//
// This version of the Connect() function takes a pointer to a
// string representing the host name to send the data to and
// an integer representing the service name or port number to
// connect to.
//
int CStreamSocket::Connect(LPSTR pszRemoteName, LPSTR pszRemoteService)
{
int nStatus = CWINSOCK_NOERROR; // error status
LPHOSTENT pHent; // pointer to host entry structure
LPSERVENT pSent; // pointer to service entry structure
SOCKADDR_IN sinRemote; // Internet address of destination
while (1)
{
// assign the address family
sinRemote.sin_family = AF_INET;
// assign the service port (may have to do a database lookup
// if a service port number was not specified)
sinRemote.sin_port = htons(atoi(pszRemoteService));
if (sinRemote.sin_port == 0)
{
pSent = getservbyname(pszRemoteService, "tcp");
if (pSent == NULL)
{
m_nLastError = WSAGetLastError();
nStatus = CWINSOCK_WINSOCK_ERROR;
break;
}
sinRemote.sin_port = pSent->s_port;
}
// assign the IP address (may have to do a database lookup
// if a dotted decimal IP address was not specified)
sinRemote.sin_addr.s_addr = inet_addr(pszRemoteName);
if (sinRemote.sin_addr.s_addr == INADDR_NONE)
{
pHent = gethostbyname(pszRemoteName);
if (pHent == NULL)
{
m_nLastError = WSAGetLastError();
nStatus = CWINSOCK_WINSOCK_ERROR;
break;
}
sinRemote.sin_addr.s_addr = *(u_long *)pHent->h_addr;
}
// call the version of Connect() that takes an
// Internet address structure
return Connect(&sinRemote);
}
return nStatus;
}
/////////////////////////////////////////////////////////////////////////////
// CStreamSocket::Connect()
//
// Connect the client socket to a server specified by the
// Internet address.
//
// This version of the Connect() function takes a pointer
// to an Internet address structure to connect to.
//
int CStreamSocket::Connect(LPSOCKADDR_IN psinRemote)
{
int nStatus = CWINSOCK_NOERROR;
while (1)
{
// only clients should call connect
if (m_bServer)
{
nStatus = CWINSOCK_PROGRAMMING_ERROR;
break;
}
// copy the Internet address of the remote server to connect to
memcpy(&m_sinRemote, psinRemote, sizeof(SOCKADDR_IN));
// attempt the asynchronous connect
if (connect(m_s, (LPSOCKADDR)&m_sinRemote, sizeof(SOCKADDR_IN)) ==
SOCKET_ERROR)
{
m_nLastError = WSAGetLastError();
if (m_nLastError == WSAEWOULDBLOCK)
m_nLastError = 0;
else
nStatus = CWINSOCK_WINSOCK_ERROR;
break;
}
break;
}
return nStatus;
}
/////////////////////////////////////////////////////////////////////////////
// CStreamSocket::Accept()
//
// Accept a connection request from a client.
//
// This function takes a pointer to a CStreamSocket object. This
// pointer will become the newly connected socket.
//
int CStreamSocket::Accept(CStreamSocket *pStreamSocket)
{
int nStatus = CWINSOCK_NOERROR;
while (1)
{
// must have valid CStreamSocket object pointer passed in
if (pStreamSocket == NULL)
{
ASSERT(0);
nStatus = CWINSOCK_PROGRAMMING_ERROR;
break;
}
// only servers should call accept
if (!m_bServer)
{
nStatus = CWINSOCK_PROGRAMMING_ERROR;
break;
}
// Make sure the socket isn't already created.
// If the socket handle is valid, return from this
// function right away so the existing parameters of
// the object are not tampered with.
if (pStreamSocket->m_s != INVALID_SOCKET)
return CWINSOCK_PROGRAMMING_ERROR;
pStreamSocket->InitVars();
// create the hidden window
RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = 100;
rect.bottom = 100;
if (pStreamSocket->Create(NULL, NULL, WS_OVERLAPPEDWINDOW, rect,
pStreamSocket->m_pParentWnd, 0) == 0)
{
nStatus = CWINSOCK_WINDOWS_ERROR;
break;
}
// accept the client connection
pStreamSocket->m_s = accept(m_s, NULL, NULL);
if (pStreamSocket->m_s == INVALID_SOCKET)
{
m_nLastError = WSAGetLastError();
nStatus = CWINSOCK_WINSOCK_ERROR;
pStreamSocket->DestroyWindow();
break;
}
// start asynchronous event notification
long lEvent;
lEvent = FD_READ | FD_WRITE | FD_CONNECT | FD_CLOSE;
if (WSAAsyncSelect(pStreamSocket->m_s, pStreamSocket->m_hWnd,
CWINSOCK_EVENT_NOTIFICATION, lEvent) == SOCKET_ERROR)
{
m_nLastError = WSAGetLastError();
nStatus = CWINSOCK_WINSOCK_ERROR;
closesocket(pStreamSocket->m_s);
pStreamSocket->DestroySocket();
break;
}
break;
}
// if anything failed in this function, set the
// socket variables appropriately
if (nStatus == CWINSOCK_WINSOCK_ERROR)
pStreamSocket->InitVars(FALSE);
else if (nStatus == CWINSOCK_NOERROR)
// notify the parent if the connection was accepted successfully
pStreamSocket->m_pParentWnd->PostMessage(pStreamSocket->m_uMsg,
CWINSOCK_YOU_ARE_CONNECTED);
return nStatus;
}
/////////////////////////////////////////////////////////////////////////////
// CStreamSocket::Write()
//
// Write data to the socket..
//
// This function takes an integer representing the length of the
// data to send and a pointer to the data to send.
//
// The data pointed to by pData must remain valid until either
// the Write() function returns with an error, or the
// write's completion is notified by the m_uMsg being sent
// to the window that owns this datagram object with wParam set
// to CWINSOCK_DONE_WRITING or CWINSOCK_ERROR_WRITING.
//
int CStreamSocket::Write(int nLen, LPVOID pData)
{
int nStatus = CWINSOCK_NOERROR;
while (1)
{
// dynamically allocate a structure to hold the
// data pointer and the data's length
LPSTREAMDATA pStreamData = new STREAMDATA;
if (pStreamData == NULL)
{
nStatus = CWINSOCK_WINDOWS_ERROR;
break;
}
pStreamData->pData = pData;
pStreamData->nLen = nLen;
// add the data to the list
TRY
{
m_listWrite.AddTail(pStreamData);
}
CATCH (CMemoryException, e)
{
delete pStreamData;
nStatus = CWINSOCK_WINDOWS_ERROR;
break;
}
END_CATCH
// trigger the FD_WRITE handler to try to send
PostMessage(CWINSOCK_EVENT_NOTIFICATION, m_s, WSAMAKESELECTREPLY(FD_WRITE, 0));
break;
}
return nStatus;
}
/////////////////////////////////////////////////////////////////////////////
// CStreamSocket::Read()
//
// Read data that has been received by the socket.
//
// This function takes a pointer to an integer that will be filled
// with the length of the data read.
//
// A pointer to the data is returned on success. The application
// using this object must free this pointer. NULL is returned on failure.
//
LPVOID CStreamSocket::Read(LPINT pnLen)
{
LPVOID pData = NULL;
// check to see if there is data to retrieve
if (!m_listRead.IsEmpty())
{
// remove the stream data from the list
LPSTREAMDATA pStreamData = (LPSTREAMDATA)m_listRead.RemoveHead();
pData = pStreamData->pData;
*pnLen = pStreamData->nLen;
delete pStreamData;
}
return pData;
}
// message map
BEGIN_MESSAGE_MAP(CStreamSocket, CWnd)
//{{AFX_MSG_MAP(CStreamSocket)
//}}AFX_MSG_MAP
ON_MESSAGE(CWINSOCK_EVENT_NOTIFICATION, OnWinSockEvent)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CStreamSocket::OnWinSockEvent()
//
// Called when there is an asynchronous event on the socket.
//
LONG CStreamSocket::OnWinSockEvent(WPARAM wParam, LPARAM lParam)
{
// check for an error
if (WSAGETSELECTERROR(lParam) != 0)
return 0L;
// what event are we being notified of?
switch (WSAGETSELECTEVENT(lParam))
{
case FD_READ:
return HandleRead(wParam, lParam);
break;
case FD_WRITE:
return HandleWrite(wParam, lParam);
break;
case FD_ACCEPT:
// tell the parent window that a client would like to connect
// to the server socket
m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_READY_TO_ACCEPT_CONNECTION);
break;
case FD_CONNECT:
// tell the parent window that the socket has connected
m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_YOU_ARE_CONNECTED);
break;
case FD_CLOSE:
// check for more data queued on the socket
// (don't tell the application that the socket is closed
// until all data has been read and notification has been posted)
if (HandleRead(wParam, lParam))
{
// fake the close event to try again
PostMessage(CWINSOCK_EVENT_NOTIFICATION, wParam, lParam);
break;
}
// tell the parent window that the socket is closed
m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_LOST_CONNECTION);
break;
default:
// this should never happen
ASSERT(0);
break;
}
return 0L;
}
/////////////////////////////////////////////////////////////////////////////
// CStreamSocket::HandleRead()
//
// Called when there is an asynchronous read event on the socket.
//
// If the read was successful, the data and its length are stored
// in the read queue. Upon a successful read, the application
// window using this object is then notified with the m_uMsg message
// (wParam set to CWINSOCK_DONE_READING; lParam set to the number of
// data chunks in the read queue). At this point, the application
// should call Read(). If the read fails for some reason, the m_uMsg
// is sent with wParam set to CWINSOCK_ERROR_READING.
//
LONG CStreamSocket::HandleRead(WPARAM wParam, LPARAM lParam)
{
while (1)
{
// allocate memory for incoming data
LPVOID pData = malloc(READ_BUF_LEN);
LPSTREAMDATA pStreamData = new STREAMDATA;
if ((pData == NULL) || (pStreamData == NULL))
{
// free anything that was allocated
if (pData != NULL)
free(pData);
pData = NULL;
if (pStreamData != NULL)
delete pStreamData;
pStreamData = NULL;
// tell the parent that a possible data read failed
m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_READING);
// fake the event to try again
PostMessage(CWINSOCK_EVENT_NOTIFICATION, m_s,
WSAMAKESELECTREPLY(FD_READ, 0));
break;
}
// receive data
int nBytesRead = recv(m_s, (LPSTR)pData, READ_BUF_LEN, 0);
if (nBytesRead == SOCKET_ERROR)
{
// free memory for incoming data
free(pData);
pData = NULL;
delete pStreamData;
pStreamData = NULL;
// if the error is just that the read would block,
// don't do anything; we'll get another FD_READ soon
m_nLastError = WSAGetLastError();
if (m_nLastError == WSAEWOULDBLOCK)
m_nLastError = 0;
else
// tell the parent that a data read failed
m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_READING);
break;
}
// make sure some data was read
if (nBytesRead == 0)
{
// free memory for incoming data
free(pData);
pData = NULL;
delete pStreamData;
pStreamData = NULL;
break;
}
// add the data to the list
pStreamData->pData = pData;
pStreamData->nLen = nBytesRead;
TRY
{
m_listRead.AddTail(pStreamData);
}
CATCH (CMemoryException, e)
{
free(pData);
pData = NULL;
delete pStreamData;
pStreamData = NULL;
// tell the parent that a data read failed
m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_READING);
break;
}
END_CATCH
// tell the parent that data has been read
m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_DONE_READING,
(LPARAM)m_listRead.GetCount());
// 1 is returned if there is data so CStreamSocket::OnWinSockEvent()'s
// FD_CLOSE handler will know when the socket can really be closed
return 1L;
break;
}
return 0L;
}
/////////////////////////////////////////////////////////////////////////////
// CStreamSocket::HandleWrite()
//
// Called when there is an asynchronous write event on the socket.
//
// If there is data in the write queue waiting to be sent,
// a WinSock send is attempted. If the send is successful,
// a m_uMsg message is sent to the application window with
// wParam set to CWINSOCK_DONE_WRITING and lParam set to the
// address of the data that was sent. On send failure,
// wParam is set to CWINSOCK_ERROR_WRITING and lParam set to
// the address of the data which couldn't be sent. In either
// case, the application may free the pointer pointing to
// the data or reuse that data buffer. It is possible for the
// entire amount of data to not be sent in one call to send().
// In this case, an attempt is made to send the remaining portion
// of that block of data the next time HandleWrite() is invoked.
//
//
LONG CStreamSocket::HandleWrite(WPARAM wParam, LPARAM lParam)
{
LPSTREAMDATA pStreamData; // pointer to stream data structure
LPVOID pData; // pointer to buffer to send
int nLen; // total length of buffer to send
static LPVOID pDataRemaining = NULL; // pointer into buffer to send
static int nLenRemaining = 0; // number of bytes left to send
while (1)
{
// check to see if there is any data to send
if (m_listWrite.IsEmpty())
break;
// if we are not in the middle of another buffer send,
// get data and data length from the write queue
pStreamData = (LPSTREAMDATA)m_listWrite.GetHead(); // not RemoveHead()
pData = pStreamData->pData;
nLen = pStreamData->nLen;
if (pDataRemaining == NULL)
{
pDataRemaining = pData;
nLenRemaining = nLen;
}
// send the data
BOOL bRemove = FALSE; // remove data from queue?
int nBytesSent = send(m_s, (LPCSTR)pDataRemaining, nLenRemaining, 0);
if (nBytesSent == SOCKET_ERROR)
{
// if the error is just that the send would block,
// don't do anything; we'll get another FD_WRITE soon
m_nLastError = WSAGetLastError();
if (m_nLastError == WSAEWOULDBLOCK)
m_nLastError = 0;
else
{
bRemove = TRUE;
m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_WRITING,
(LPARAM)pData);
}
}
else
{
// if data was sent, we must still check to see
// if all the bytes were sent
if (nBytesSent == nLenRemaining)
{
bRemove = TRUE;
m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_DONE_WRITING,
(LPARAM)pData);
}
else
{
// the complete buffer was not sent so adjust
// these values accordingly
pDataRemaining = (LPVOID)((LPCSTR)pDataRemaining + nBytesSent);
nLenRemaining = nLenRemaining - nBytesSent;
}
}
// if the data was completely sent or there was
// a real error, remove the data from the queue
if (bRemove)
{
delete pStreamData;
m_listWrite.RemoveHead();
pDataRemaining = NULL;
nLenRemaining = 0;
}
// if there is more data to send, trigger this FD_WRITE handler
if (!m_listWrite.IsEmpty())
PostMessage(CWINSOCK_EVENT_NOTIFICATION, m_s,
WSAMAKESELECTREPLY(FD_WRITE, 0));
break;
}
return 0L;
}
/////////////////////////////////////////////////////////////////////////////
// CStreamSocket::GetPeerName()
//
// Copies the Internet address of the other end of the socket
// connection into the pointer provided.
// Useful for server's to use after an Accept().
//
int CStreamSocket::GetPeerName(LPSOCKADDR_IN psinRemote)
{
int nStatus = CWINSOCK_NOERROR;
int nLen = sizeof(SOCKADDR_IN);
// make sure the listening socket doesn't call this function
if (m_bServer)
nStatus = CWINSOCK_PROGRAMMING_ERROR;
else if (getpeername(m_s, (LPSOCKADDR)psinRemote, &nLen) == SOCKET_ERROR)
{
m_nLastError = WSAGetLastError();
nStatus = CWINSOCK_WINSOCK_ERROR;
}
return nStatus;
}
/////////////////////////////////////////////////////////////////////////////
// Utility functions
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// CWinSockErrorBox
//
void CWinSockErrorBox(int nError, LPSTR pszMessage/*= NULL*/)
{
#define ERROR_BUF_LEN (1000)
char pszError[ERROR_BUF_LEN];
wsprintf(pszError, "WinSock error %d: ", nError);
switch (nError)
{
case WSAEINTR:
lstrcat(pszError, "Interrupted system call");
break;
case WSAEBADF:
lstrcat(pszError, "Bad file number");
break;
case WSAEACCES:
lstrcat(pszError, "Permission denied");
break;
case WSAEFAULT:
lstrcat(pszError, "Bad address");
break;
case WSAEINVAL:
lstrcat(pszError, "Invalid argument");
break;
case WSAEMFILE:
lstrcat(pszError, "Too many open files");
break;
case WSAEWOULDBLOCK:
lstrcat(pszError, "Operation would block");
break;
case WSAEINPROGRESS:
lstrcat(pszError, "Operation now in progress");
break;
case WSAEALREADY:
lstrcat(pszError, "Operation already in progress");
break;
case WSAENOTSOCK:
lstrcat(pszError, "Socket operation on non-socket");
break;
case WSAEDESTADDRREQ:
lstrcat(pszError, "Destination address required");
break;
case WSAEMSGSIZE:
lstrcat(pszError, "Message too long");
break;
case WSAEPROTOTYPE:
lstrcat(pszError, "Protocol wrong type for socket");
break;
case WSAENOPROTOOPT:
lstrcat(pszError, "Protocol not available");
break;
case WSAEPROTONOSUPPORT:
lstrcat(pszError, "Protocol not supported");
break;
case WSAESOCKTNOSUPPORT:
lstrcat(pszError, "Socket type not supported");
break;
case WSAEOPNOTSUPP:
lstrcat(pszError, "Operation not supported on socket");
break;
case WSAEPFNOSUPPORT:
lstrcat(pszError, "Protocol family not supported");
break;
case WSAEAFNOSUPPORT:
lstrcat(pszError, "Address family not supported by protocol family");
break;
case WSAEADDRINUSE:
lstrcat(pszError, "Address already in use");
break;
case WSAEADDRNOTAVAIL:
lstrcat(pszError, "Can't assign requested address");
break;
case WSAENETDOWN:
lstrcat(pszError, "Network is down");
break;
case WSAENETUNREACH:
lstrcat(pszError, "Network is unreachable");
break;
case WSAENETRESET:
lstrcat(pszError, "Network dropped connection on reset");
break;
case WSAECONNABORTED:
lstrcat(pszError, "Software caused connection abort");
break;
case WSAECONNRESET:
lstrcat(pszError, "Connection reset by peer");
break;
case WSAENOBUFS:
lstrcat(pszError, "No buffer space available");
break;
case WSAEISCONN:
lstrcat(pszError, "Socket is already connected");
break;
case WSAENOTCONN:
lstrcat(pszError, "Socket is not connected");
break;
case WSAESHUTDOWN:
lstrcat(pszError, "Can't send after socket shutdown");
break;
case WSAETOOMANYREFS:
lstrcat(pszError, "Too many references: can't splice");
break;
case WSAETIMEDOUT:
lstrcat(pszError, "Connection timed out");
break;
case WSAECONNREFUSED:
lstrcat(pszError, "Connection refused");
break;
case WSAELOOP:
lstrcat(pszError, "Too many levels of symbolic links");
break;
case WSAENAMETOOLONG:
lstrcat(pszError, "File name too long");
break;
case WSAEHOSTDOWN:
lstrcat(pszError, "Host is down");
break;
case WSAEHOSTUNREACH:
lstrcat(pszError, "No route to host");
break;
case WSAENOTEMPTY:
lstrcat(pszError, "Directory not empty");
break;
case WSAEPROCLIM:
lstrcat(pszError, "Too many processes");
break;
case WSAEUSERS:
lstrcat(pszError, "Too many users");
break;
case WSAEDQUOT:
lstrcat(pszError, "Disc quota exceeded");
break;
case WSAESTALE:
lstrcat(pszError, "Stale NFS file handle");
break;
case WSAEREMOTE:
lstrcat(pszError, "Too many levels of remote in path");
break;
#ifdef _WIN32
case WSAEDISCON:
lstrcat(pszError, "Disconnect");
break;
#endif
case WSASYSNOTREADY:
lstrcat(pszError, "Network sub-system is unusable");
break;
case WSAVERNOTSUPPORTED:
lstrcat(pszError, "WinSock DLL cannot support this application");
break;
case WSANOTINITIALISED:
lstrcat(pszError, "WinSock not initialized");
break;
case WSAHOST_NOT_FOUND:
lstrcat(pszError, "Host not found");
break;
case WSATRY_AGAIN:
lstrcat(pszError, "Non-authoritative host not found");
break;
case WSANO_RECOVERY:
lstrcat(pszError, "Non-recoverable error");
break;
case WSANO_DATA:
lstrcat(pszError, "Valid name, no data record of requested type");
break;
default:
lstrcpy(pszError, "Not a WinSock error");
break;
}
lstrcat(pszError, "\n");
int n = lstrlen(pszError);
if (pszMessage != NULL)
n += lstrlen(pszMessage);
if ((pszMessage != NULL) && (n < ERROR_BUF_LEN))
lstrcat(pszError, pszMessage);
AfxMessageBox(pszError);
}